/**
 * @Copyright 2012, Founder Mobile Media Technology（Chongqing） Co., Ltd L.P. All
 *            rights reserved. The information contained herein is confidential
 *            and proprietary to Founder Mobile Media Technology（Chongqing） Co.,
 *            Ltd, and considered a trade secret as defined under civil and
 *            criminal statutes. Founder Mobile Media Technology（Chongqing） Co.,
 *            Ltd shall pursue its civil and criminal remedies in the event of
 *            unauthorized use or misappropriation of its trade secrets. Use of
 *            this information by anyone other than authorized employees of
 *            Founder Mobile Media Technology（Chongqing） Co., Ltd is granted
 *            only under a written non-disclosure agreement, expressly
 *            prescribing the scope and manner of such use.
 */
package com.g2room.io.file.randomAccessFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

import com.g2room.io.file.BytesEncodingDetect;

/**
 * 
 *
 * @createTime: Jun 15, 2013 9:57:38 PM
 * @author: <a href="mailto:gaoxg@FOUNDER.COM.CN">Xuguo Gao</a>
 * @version: 0.1
 * @lastVersion: 0.1
 * @updateTime: 
 * @updateAuthor: <a href="mailto:gaoxg@FOUNDER.COM.CN">Xuguo Gao</a>
 * @changesSum: 
 * 
 */
public class RandomAccessFileTest {
	private String fileDir = "d:/workspace/code/Java_Win7/Tests/src/com/g2room/file/data/";
	private String  largeFilePath = fileDir + "largeFile.txt";
	public static void main(String[] args) {
		long startTime = System.currentTimeMillis();
		RandomAccessFileTest randomAccessFileTest = new RandomAccessFileTest();
		try {
//			try {
//				Thread.sleep(35000);
//			} catch (InterruptedException e) {
//				// TODO Auto-generated catch block
//				e.printStackTrace();
//			}
//			randomAccessFileTest.largeFileWrite();
			randomAccessFileTest.readLargeFile();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("耗时为：" + (System.currentTimeMillis() - startTime) + "毫秒");
//		long size = 0;
//		try {
//			size = randomAccessFileTest.readAndWriteFile();
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		System.out.println("文件大小为：" + (size/1024/1024) + "M，耗时为：" + (System.currentTimeMillis() - startTime) + "毫秒");
    }
	
	public void readLargeFile() throws IOException{
		largeFilePath = "E:/var/test/logs/info/Info.txt";
		long start = Runtime.getRuntime().freeMemory();
		File file = new File(largeFilePath);
		RandomAccessFile raf = new RandomAccessFile(file, "r");
		int i = 0;
		
		BytesEncodingDetect encode = new BytesEncodingDetect();
		String charset = BytesEncodingDetect.javaname[encode.detectEncoding(new File(largeFilePath))];
		System.out.println(charset);
		boolean flag = true;
		while(raf.getFilePointer() < raf.length()) {
			byte[] tempBytes = raf.readLine().getBytes("iso-8859-1");
			byte[] contentBytes = tempBytes;
			if(flag) {
				// 判断是否包含EFBBBF的UTF-8头部BOM
				String hexStr = "";
				for (int j = 0; j < 3; j++) {
					String tempHexStr = Integer.toHexString(tempBytes[j]);
					// 1个byte变成16进制的，只需要2位就可以表示了，取后面两位，去掉前面的符号填充
					hexStr += tempHexStr.substring(tempHexStr.length() -2);
				}
				if(hexStr.toUpperCase().equals("EFBBBF")) {
					contentBytes = new byte[tempBytes.length - 3];
					System.arraycopy(tempBytes, 3, contentBytes, 0, tempBytes.length - 3);
				}
				flag = false;
			}

			System.out.println(new String(contentBytes, "UTF-16"));
			i++;
			if(i>10) break;
		}
		long end = Runtime.getRuntime().freeMemory();
		System.out.println("\n\nused  menory:" + (start - end) / 1024 + "KB");
	    raf.close();
	}
	
	/**
	 * 大数据写入到文件
	 *
	 * @throws IOException
	 * @auther <a href="mailto:gaoxg@FOUNDER.COM.CN">Gao Xuguo</a>
	 * Jun 17, 2013 9:09:28 PM
	 */
	public long largeFileWrite() throws IOException {
		long startTime = System.currentTimeMillis();
		// byte编码UTF-8是3个字节表示一个汉字，GBK是2个字节表示一个汉字
		// 字母、数字、符号都是一个字节表示一个
		String encode = "GBK";
		// 设置文件内容大小
		int length = 10*1024*1024;
		// 为了以可读可写的方式打开文件，这里使用RandomAccessFile来创建文件。
		FileChannel fc = new RandomAccessFile(largeFilePath, "rw").getChannel();
		// 写入的内容
		String content = "2013-07-05 17:06:38 ID 发送第5000送条记录，耗时10806毫秒。每秒462条记录，每条2毫秒\n";
		byte[] bytes = content.getBytes();
		// 注意，文件通道的可读可写要建立在文件流本身可读写的基础之上
		MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, 0, length);
		int j = 0;
		for (int i = 0; i < length - bytes.length;) {
			j++;
			bytes = content.replace("ID", j+"").getBytes();
			out.put(bytes);
			i+= bytes.length;
		}
		System.out.println("写入完毕，耗时：" + (System.currentTimeMillis() - startTime) +"毫秒");
		bytes = new byte[12];
		// 读取文件开始12个字节内容
		for (int i = 0; i < 12; i++) {
			bytes[i] = out.get(i);
		}
		System.out.println(new String(bytes,encode));
		long size = fc.size();
		fc.close();
		return size;
	}
	/**
	 * 对文件的读写
	 *
	 * @throws IOException
	 * @auther <a href="mailto:gaoxg@FOUNDER.COM.CN">Gao Xuguo</a>
	 * Jun 17, 2013 9:18:09 PM
	 */
	public long readAndWriteFile() throws IOException {
		long size = 0;
		String filePath = fileDir + "write.txt";
		RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "rw");
		for (int i = 0; i < 10; i++) {
          //写入基本类型double数据
			randomAccessFile.writeUTF(""+i * 1.414);
		}
		randomAccessFile.close();
		randomAccessFile = new RandomAccessFile(filePath, "rw");
		//直接将文件指针移到第5个double数据后面
		randomAccessFile.seek(5 * 8);
		//覆盖第6个double数据
		randomAccessFile.writeUTF("====合抱之木，生于毫末；九层之台，起于累土；千里之行，始于足下。");
		size = randomAccessFile.length();
		System.out.println("size==" + size);
	    randomAccessFile.close();
	    randomAccessFile = new RandomAccessFile(filePath, "r");
	    for (int i = 0; i < 2; i++) {
	        System.out.println("Value " + i + ": " + randomAccessFile.readUTF());//.readDouble());  
	    }
	    randomAccessFile.close();
	    return size;
	}
	
	/**
	 * 复制文件，此种方式适合复制比较小的文件
	 *
	 * @throws IOException
	 * @auther <a href="mailto:gaoxg@FOUNDER.COM.CN">Gao Xuguo</a>
	 * Jun 18, 2013 10:44:44 AM
	 */
	public void copyFile() throws IOException {
		String dir = fileDir + "main";
		File dirFile = new File(dir);
		File[] files = dirFile.listFiles();
		for(File file : files) {
			RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
			RandomAccessFile randomAccessFileCopy = new RandomAccessFile(fileDir + "backup/" + file.getName(), "rw");
			// 取得文件长度（字节数）
			int len = (int) randomAccessFile.length();
			byte[] b = new byte[len];
			randomAccessFile.readFully(b);
			randomAccessFileCopy.write(b);
		}
		System.out.println("复制完成！");
	}
	
	/**
	 * 复制文件，此种方式适合复制大文件
	 *
	 * @param {int} m 缓存大小，以M为单位
	 * @throws IOException
	 * @auther <a href="mailto:gaoxg@FOUNDER.COM.CN">Gao Xuguo</a>
	 * Jun 18, 2013 11:40:21 AM
	 */
	public void channelCopy(int m) throws IOException {
		String dir = "F:/电影/temp";
		File dirFile = new File(dir);
		File[] files = dirFile.listFiles();
		long start = System.currentTimeMillis();
		long size = 0;
		for(File file : files) {
			size += file.length();
			System.out.println((size/1024/1024) + "M，文件" + file.getName() + "大小为：" + (file.length() / 1024/1024) + "M");
			//文件缓冲字节区，大小可以自己定
			int bsize = m*1024*1024;
			//得到输入通道
	        FileChannel in = new FileInputStream(file).getChannel();
	        //得到输出通道
	        FileChannel out = new FileOutputStream(fileDir + "backup/" + file.getName()).getChannel();  
	        //设定缓冲区
	        ByteBuffer buffer = ByteBuffer.allocate(bsize);
	        while(in.read(buffer) != -1){
	        	//准备写入，防止其他读取，锁住文件
	            buffer.flip();
	            out.write(buffer);
	            //准备读取。将缓冲区清理完毕，移动文件内部指针
	            buffer.clear();
	        }
		}
		long time = System.currentTimeMillis() - start;
		System.out.println("耗时："+time+"毫秒，速度：" + (size/1024/1024/(time /1000)) + "M/S");
	}
	
	public void MultiWrite() throws IOException {
		int block = 50;
		long startTime = System.currentTimeMillis();
		String filePath = "F:/电影/temp/中国合伙人BD.rmvb";
		String targetFilePath = fileDir + "backup/中国合伙人BD.rmvb";
		File file = new File(filePath);
		long length = file.length();
		// 预分配文件所占的磁盘空间，磁盘中会创建一个指定大小的文件
		RandomAccessFile raf = new RandomAccessFile(targetFilePath, "rw");
		// 预分配文件空间
		raf.setLength(length);
		System.out.println("创建文件成功，文件大小为：" + raf.length());
		raf.close();
		long pos = 0;
		
		// 记录结尾部分数据
		long endLength = 0;
		long blockLen = 0;
		if(length % block > 0) {
			long temp = length % (block - 1);
			if(temp == 0) {
				blockLen = length / (block - 1) - 1;
				endLength = block - 1;
			} else {
				blockLen = (length - endLength) / (block - 1);
				endLength = temp;
			}
		} else {
			blockLen = (length - endLength) / block;
		}
		
		for (int i = 0; i < block; i++) {
			pos = i * blockLen;
			System.out.println(i+"开始分块写入文件，pos="+pos + ",limit="+blockLen);
			new FileWriteThread(pos, i==block-1?endLength:blockLen, filePath, targetFilePath).start();
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("写入完成！耗时："+(System.currentTimeMillis() - startTime)+"毫秒");
	}
	
	// 利用线程在文件的指定位置写入指定数据
	static class FileWriteThread extends Thread {
		private long start;
		private long limit;
		private String sourcePath;
		private String targetPath;

		public FileWriteThread(long start,long limit, String sourcePath, String targetPath) {
			this.start = start;
			this.limit = limit;
			this.sourcePath = sourcePath;
			this.targetPath = targetPath;
		}

		public void run() {
			RandomAccessFile outRaf = null;
			RandomAccessFile inRaf = null;
			FileChannel outChannel = null;
			try {
				outRaf = new RandomAccessFile(this.sourcePath, "r");
				outChannel = outRaf.getChannel();
				System.out.println("outRaf.length="+outRaf.length()+",start:"+start+", limit："+limit);
				inRaf = new RandomAccessFile(this.targetPath, "rw");
				outRaf.seek(start);
				inRaf.seek(start);
				byte[] bytes = new byte[(int) limit];
				System.out.println("(int)start="+(int)start+",(int)limit:"+(int)limit);
				outRaf.readFully(bytes, 0, (int)limit);
				inRaf.write(bytes);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					if(null != inRaf)inRaf.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
				try {
					if(null != outChannel) outChannel.close();
				} catch (IOException e) {
					e.printStackTrace();
				};
				try {
					if(null != outRaf) outRaf.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
}
